In previous chapter, we learned numerical
calculations in scalar operations.
In this chapter, we will consider how this concept
extends to arrays. However, there are also complicated
operations that requires the “operator”, I’ll explain
those use-cases in next chapter.
Today, many of general-purpose programming languages
handle arrays with the procedural method. However, this
approach is incompatible with that of vector algebra.
The APL-family languages including AROAPL have already
solved this problem with the mathematical notation, and
parallel calculation called SIMD. Therefore, it is very
simple and easy to understand what the program wants to
do for programmers read it and for computers execute
it.
Though vector and matrix are the part of curriculum of
mathematics for 12th graders and first-year
undergraduate students, their fundamental principles and
operations are very simple, so there is no need to
worry.
3.1. Vector Array
It is not difficult to understand what “array”
is.
Array is set of values as a variable. For example, in
case of integer array:
ARR: ι[5] ← 5 ¯2 8 17 0;
At this point, we don’t use delimiters such as [ ], { }, or ( ) when defining an array. Even in case of high dimentional arrays, too. I’ll explain it later.
Also, please be careful not to define different typed values in an array:
⎕ ← 5 "A" ⊤ 5. 5.J5.;
VALUE ERROR: ALL ELEMENTS MUST BE THE SAME TYPE
3.1.1. Sequence
In mathematical terms, array equals “sequence”. Therefore, a rearranged array is not considered to be the same as the original:
⎕ ← 5 ¯2 8 17 0 ≡ 0 ¯2 17 5 8;
⊥
By the way, if you calculate with not using
≡ (match) but using = (equal),
each elements are compared:
⎕ ← 5 ¯2 8 17 0 = 0 ¯2 5 17 8;
⊥ ⊤ ⊥ ⊤ ⊥
3.1.2. Retrieving with an Index
In other words, since an array is a sequence of
elements, you can specify an index to retrieve its
value.
However, please be sure to keep in mind that the indices
start at 0.
A ← 5 ¯2 8 17 0;
⎕ ← A[0];
⎕ ← A[1];
⎕ ← A[2];
⎕ ← A[3];
⎕ ← A[4];
5
¯2
8
17
0
If you define out of range of an array, it returns error:
A ← 5 ¯2 8 17 0;
⎕ ← A[5];
INDEX OUT OF RANGE ERROR: LENGTH OF ARRAY IS 5, BUT YOU INDEXED 5
And you may set negative index:
A ← 5 ¯2 8 17 0;
⎕ ← A[¯1];
⎕ ← A[¯2];
⎕ ← A[¯3];
⎕ ← A[¯4];
⎕ ← A[¯5];
0
17
8
¯2
5
Of course, about out of range:
A ← 5 ¯2 8 17 0;
⎕ ← A[¯6];
INDEX OUT OF RANGE ERROR: SIZE OF ARRAY IS 5, BUT YOU INDEXED ¯6
Additionally, it is one of interesting things in AROAPL, we can extract multi indexes as a new array:
A ← 5 ¯2 8 17 0;
⎕ ← A[0 ¯1 1 ¯2 2 4 ¯5 3 3];
5 0 ¯2 17 ¯2 0 5 17 17
3.1.3. Size of Array
To get size of array, ρ (get shape)
monad function:
A ← 5 ¯2 8 17 0;
⎕ ← ρA;
5
3.1.4. Type Definition
When defining variables with type, we can declare the size of array as either fixed or dynamic:
A: ι[5] ← 5 ¯2 8 17 0; ⍝ FIXED ⍝
B: ι[?] ← 5 ¯2 8 17 0; ⍝ DYNAMIC ⍝
⎕ ← A;
⎕ ← B;
⎕ ← "";
⎕ ← ρA;
⎕ ← ρB;
5 ¯2 8 17 0
5 ¯2 8 17 0
5
5
In case of fixed, if size of array are different to specified size, it is error:
A: ι[4] ← 5 ¯2 8 17 0;
SIZE OF ARRAY ERROR: IT IS NOT AN ARRAY OF SPECIFIED LENGTH 5 → 4
And in case of dynamic, it must not be happened bacause size is decided after declaired array.
The reason dynamic type definition are also necessary
is that they offer various benefits.
To begin with, there are cases that we cannot decide
size until variable declaired. For example, string,
contents of files, hit cases, and so on.
3.1.5. Array Generation
If you want to get ordered-indexes array,
ι (index array generator) monad function is
convinience:
⎕ ← ι4;
0 1 2 3
Moreover, if you want an shuffled-indexes array.
? (random index array generator) monad
function is useful:
⎕ ← ?6;
4 1 0 2 3 5
3.1.6. Extending Monadic Functions to Array
If we extend a monadic function applied to scalar to array, each values are applied.
⎕ ← - ¯2. 3.43 8. ¯2.66
2. ¯3.43 ¯8. 2.66
But they are the exceptions: ι (ordered
index array generator), ρ (shape
extractor), = (hit cases), ⌹
(matrix inversion), ⍉ (matrix
transposition), and monadic operators.
Except them, we extend others to that, too.
Also, high dimentional array works, too.
3.1.7. Extending Dyadic Functions to Array
There are two approaches to extending dyadic functions.
For dyadic operations between scalar and array, scalar applies to each values of array.
⎕ ← ¯6 + 4 1 0 2 3 5;
¯2 ¯5 ¯6 ¯4 ¯3 ¯1
For dyadic operations between array and array that both are the same size, both each values on the same index are operated.
⎕ ← 4 1 0 2 3 5 + ¯3 5 5 8 8 2;
1 6 5 10 11 7
If size of both arrays are different, we cannot operate.
⎕ ← ¯6 9 + 4 1 0 2 3 5;
DIFFERENT SIZED ARRAY CALCULATION: 6 2
However, only & (combination) method
is the exception. I’ll introduct it next.
3.1.8. Combine
To combine both arrays, we use &
(combination) dyad function.
It doesn’t matter if size of both arrays are
different.
⎕ ← 4 8 2 & 9 8 5 3;
4 8 2 9 8 5 3
Also, to repeat, unfortunately, we cannot overwrite
value of variable in AROAPL.
Therefore, it is not suitable to append sequentially or
iteratively. In such cases, consider using parallel
processing to apply in bulk.
3.1.9. Split
AROAPL doesn’t support split method. But, we can use extraction by indexes alternatively.
A ← 5 ¯2 8 17 0;
⎕ ← A[1 + ι3];
¯2 8 17
3.1.10. Hit Cases
There are some cases that we want to extract indexes
of hit cases.
= (hit cases) monad function is
convinience:
A ← 5 ¯2 8 17 0;
B ← 0 ¯2 5 17 8;
⎕ ← A = B;
⎕ ← = A = B;
⎕ ← A[= A = B];
⊥ ⊤ ⊥ ⊤ ⊥
1 3
¯2 17
3.2. High Dimentional Array as Tensor
Next, let’s learn how to define and operate high
dimentional array.
There are many points we require special attentions.
High dimentional array in AROAPL is always
tensor.
In vector, size of array is single element of count. In
tensor, it becomes multiply. As another explanation,
each element of tensor assigned with multiple
indexes.
Therefore, all child arrays of parent high dimentional
array must be of the same size. This principle is called
regularity.
For example, this is regular:
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
But this is not, and not allowed:
8 9 4 3
9 7
¯9 ¯8 7
Why does is do that? As the simplest of explanation, it is able to be expressed:
HA ← ι[3 4] ← ...
Also, for both of computers (memory accessor), and
programmers, regulared alignment is the most readable.
Actually, regulared high dimentional array can be
accessed super faster than not. I’ll explain it in
chapter 13.
And surely, we’ve understood irregular high dimentional
array is also essential to define and operate. We have
method to express by using tuple structure at chapter
7.
For more details, I’ll divide as sub-sections.
3.2.1. Defining with Reshape
There are two ways how to declair high dimentional array.
- reshape vector
- load CSV or DCF
I’ll explain the latter method in chapter 6.
We use ρ (reshape) dyad function.
In case of matrix,
⎕ ← 2 3 ρ 2 ¯5 8 9 2 4;
2 ¯5 8
9 2 4
In case of three dimentional array:
⎕ ← 3 2 3 ρ 1 ¯5 8 9 2 4 2 ¯5 8 9 2 4 3 ¯5 8 9 2 4;
1 ¯5 8
9 2 4
2 ¯5 8
9 2 4
3 ¯5 8
9 2 4
Of course, we can get shape.
A ← 2 3 ρ 2 ¯5 8 9 2 4;
⎕ ← ρA;
B ← 3 2 3 ρ 1 ¯5 8 9 2 4 2 ¯5 8 9 2 4 3 ¯5 8 9 2 4;
⎕ ← ρB;
2 3
3 2 3
It occurs error if we define different size:
⎕ ← 2 3 ρ 2 ¯5 8 9 2;
SIZE OF ARRAY ERROR: ARRAY IS NOT RESHAPABLE 2 3 ρ 5
3.2.2. Extracting Sub Elements with Indexes
Extracting sub elements in high dimentional array has multiple methods.
Getting single value:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← A[1, 2];
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
¯5
We need , delimiter between indexes.
This is for getting sub array:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
B ← A[0 2, 2];
C ← A[1, 0 2 3];
D ← A[0 2, 0 2 3];
⎕ ← A;
⎕ ← "";
⎕ ← B;
⎕ ← "";
⎕ ← ρB;
⎕ ← "";
⎕ ← C;
⎕ ← "";
⎕ ← ρC;
⎕ ← "";
⎕ ← D;
⎕ ← "";
⎕ ← ρD;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
4
7
2 1
9 ¯5 3
3
8 4 3
¯9 7 6
2 3
3.2.3. Extending Monadic Functions to High Dimentional Array
Basic monad functions are simple:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← -A;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
¯8 ¯9 ¯4 ¯3
¯9 ¯7 5 ¯3
9 8 ¯7 ¯6
But = (hit cases) monad function is a
littele bit special:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← 4 ≥ A;
⎕ ← "";
⎕ ← = 4 ≥ A;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
⊤ ⊤ ⊤ ⊥
⊤ ⊤ ⊥ ⊥
⊥ ⊥ ⊤ ⊤
0 0
0 1
0 2
1 0
1 1
2 2
2 3
In this case, how to get sub elements by using
extracted indexes by hit cases?
Actually, we don’t have effective methods yet to solve
it. We’ll learn in the next chapter.
3.2.4. Extending Dyadic Functions to High Dimentional Array
Between scalar and matrix:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← ¯4 + A;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
4 5 0 ¯1
5 3 ¯9 ¯1
¯13 ¯12 3 2
Between vector and matrix:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← ¯4 3 8 5 + A;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
4 12 12 8
5 10 3 8
¯13 ¯5 15 11
And, between matrix and matrix:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
B ← 3 4 ρ ¯4 3 8 5 ¯8 9 7 0 0 ¯16 ¯23 ¯36;
⎕ ← A;
⎕ ← "";
⎕ ← B;
⎕ ← "";
⎕ ← A + B;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
¯4 3 8 5
¯8 9 7 0
0 ¯16 ¯23 ¯36
4 12 12 8
1 16 2 3
¯9 ¯24 ¯16 ¯30
3.2.5. Matrix Inversion
This is a special function. There are only two:
⌹ (matrix inversion) and ⍉
(matrix transposition).
⌹ (matrix inversion) monad function is
simple. Just culculate the inverse matrix:
A ← 2 2 ρ 2. ¯3. 4. 10.;
⎕ ← A;
⎕ ← "";
⎕ ← ⌹A;
2 . ¯3.
4. 10.
0.3125 0.09375
¯0.125 0.0625
3.2.6. Matrix Transpositioning
⍉ (matrix transposition) monad function
replaces matrix as inverse indexes.
This function supports only two dimentional array
(matrix). But for extending to operator, it can support
higher dimentional tensor. Here, I’ll explain only for
matrix:
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← ⍉A;
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
8 9 ¯9
9 7 ¯8
4 ¯5 7
3 3 6
3.3. String Manipulations
If you have even a little programming experience, you may have been frustrated that we haven’t covered strings yet. Sorry to keep you waiting.
3.3.1. Defining String
Recent modern programming languages don’t take care
We don’t define string like this:
STR: κ[?] ← "H" "E" "L" "L" "O";
⎕ ← STR;
Unfortunately, this is the result:
STRING DEFINISION ERROR: STRING CANNOT EXTEND HIGH DIMENTIONAL ARRAY WITHOUT USING RESHAPE
But, like this:
STR: κ[?] ← "HELLO";
⎕ ← STR;
HELLO
Or, like this:
STR: κ[?] ← "H" & "E" & "L" & "L" & "O";
⎕ ← STR;
HELLO
Character, and string has few methods than boolean or
number.
They are only extracting by indexes, ρ
(shape extractor) monad, ρ (reshape) dyad,
& (combination) dyad, =
(equal) dyad, ≡ (match) dyad, and
⍉ (matrix transposition) dyad.
3.3.2. Embedding Variables
This is completely special operation. We can embed variables even any types, and even high dimentional array in string.
A ← 3 4 ρ 8 9 4 3 9 7 ¯5 3 ¯9 ¯8 7 6;
⎕ ← A;
⎕ ← "";
⎕ ← "MATRIX: {A}";
⎕ ← "";
⎕ ← ρ "MATRIX: {A}";
8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
MATRIX: 8 9 4 3
9 7 ¯5 3
¯9 ¯8 7 6
3 18
3.4. Empty Value
There is also “empty value”.
The easiest examples to try are ι (ordered index array
generator) or ? (shuffled index array generator). Or you
can also define it directly.
⎕ ← φ;
⎕ ← ι0;
⎕ ← ?0;
φ
φ
φ
The simplest example is actually empty string, but this requires special handling when outputting to the console.
⎕ ← "";
Because AROAPL compiler check its type even if the value is empty. Incidentally, if you define direct empty value, the type becomes inferred type or boolean.
On top of that, its handling becomes quite special in
various ways. For example, in an + (plus)
operation, it becomes an empty value.
⎕ ← 10 + φ;
φ
But it doesn’t work:
⎕ ← 10 + "";
VALUE ERROR: ALL ELEMENTS MUST BE THE SAME TYPE
In case of some monad methods, they returns the same empty value:
⎕ ← +φ;
⎕ ← -φ;
⎕ ← ×φ;
⎕ ← ÷φ;
⎕ ← ⌈φ;
⎕ ← ⌊φ;
⎕ ← ¬φ;
⎕ ← =φ;
φ
φ
φ
φ
φ
φ
φ
φ
In case of & (combination) method,
returns the same array:
⎕ ← 10 8 3 & φ;
10 8 3
And, it doesn’t work:
⎕ ← ιφ;
INDEX ARRAY GENERATION ERROR: IT MUST BE SCALAR
It doesn’t work, too:
⎕ ← ?φ;
INDEX ARRAY GENERATION ERROR: IT MUST BE SCALAR